<?php

class Server
{
    /**
     * 进程ID
     *
     * @var array
     */
    public static $pidMap = [];

    /**
     * socket连接
     *
     * @var array
     */
    protected static $listenedSockets = [];

    /**
     * worker配置
     *
     * @var array|mixed
     */
    protected static $config = [];

    public function __construct()
    {
        self::$config = require_once "config.php";
    }

    public static function run()
    {
        self::saveMasterId();
        self::listenSocket();
        self::registerSignal();
        self::forkWorkers();
        self::monitor();
    }

    public static function saveMasterId()
    {
        $pid = posix_getpid();
        file_put_contents('master.pid', $pid);
    }

    /**
     * 监听socket
     */
    protected static function listenSocket()
    {
        $context_option['socket']['backlog'] = 10240;
        $context = \stream_context_create($context_option);

        foreach (self::$config['workers'] as $config) {
            $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
            $ip = isset($config['ip']) ? $config['ip'] : "0.0.0.0";
            $error_no = 0;
            $error_msg = '';

            $worker_name = $config['service_name'];
            // 创建监听socket
            self::$listenedSockets[$worker_name] = stream_socket_server("{$config['protocol']}://{$ip}:{$config['port']}", $error_no, $error_msg, $flags, $context);
            if (!self::$listenedSockets[$worker_name]) {
                exit("创建socket失败");
            }
        }
    }

    /**
     * 创建子进程
     */
    public static function forkWorkers()
    {
        foreach (self::$config['workers'] as $config) {
            $worker_name = $config['service_name'];
            if (empty(self::$pidMap[$worker_name])) {
                self::$pidMap[$worker_name] = array();
            }
            while (count(self::$pidMap[$worker_name]) < $config['child_count']) {
                self::forkOne($worker_name);
            }
        }
    }

    public static function forkOne($worker_name)
    {
        $pid = pcntl_fork();//fork进程，从这里产生进程分支
        if ($pid > 0) {
            // 在父进程中执行
            self::$pidMap[$worker_name][$pid] = $pid;
        } elseif ($pid == 0) {
            // 在子进程中执行
            // 子进程关闭不用的监听socket
            foreach (self::$listenedSockets as $tmp_worker_name => $tmp_socket) {
                if ($tmp_worker_name != $worker_name) {
                    fclose($tmp_socket);
                }
            }
            $worker = new Worker(self::$listenedSockets[$worker_name], $worker_name);
            $worker->work();
        }
    }

    /**
     * 注册信号处理
     */
    public static function registerSignal()
    {
        $signalHandler = 'Server::signalHandler';
        pcntl_signal(\SIGINT, $signalHandler, false);
    }

    public static function signalHandler($signo)
    {
        switch ($signo) {
            case \SIGINT:
                self::stopAll();
                break;
        }
    }

    public static function stopAll()
    {
        $pid = posix_getpid();
        $masterPid = file_get_contents('master.pid');
        if ($masterPid == $pid) {
            $worker_pid_array = self::getAllWorkerPids();
            foreach ($worker_pid_array as $worker_pid) {
                \posix_kill($worker_pid, \SIGINT);
            }
        } else {
            exit(0);
        }
    }

    /**
     * master进程监听信号
     */
    public static function monitor()
    {
        foreach (self::$listenedSockets as $name => $fd) {
            fclose($fd);
        }
        while (1) {
            // 为挂起的信号调用信号处理程序
            pcntl_signal_dispatch();
            // 挂起当前进程的执行，直到子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数
            $status = 0;
            $pid = pcntl_wait($status, \WUNTRACED);
            pcntl_signal_dispatch();

            if ($pid > 0) {
                // 子进程退出后清除map
                foreach (self::$pidMap as $worker_name => $worker_pid_array) {
                    if (isset($worker_pid_array[$pid])) {
                        unset(self::$pidMap[$worker_name][$pid]);
                    }
                }
            }

            if (!self::getAllWorkerPids()) {
                // 直到子进程全部退出，主进程退出
                exit(0);
            }
        }
    }

    protected static function getAllWorkerPids()
    {
        $pid_array = array();
        foreach (self::$pidMap as $worker_pid_array) {
            foreach ($worker_pid_array as $worker_pid) {
                $pid_array[$worker_pid] = $worker_pid;
            }
        }
        return $pid_array;
    }
}